﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PowerPoint = Microsoft.Office.Interop.PowerPoint;
using System.ComponentModel;
using System.Reflection;
using ThreeByte.ActiveDeck.AddIn.Content.Controls;

namespace ThreeByte.ActiveDeck.AddIn.Config.Properties
{
    public class PropertyTagBinder : ICustomTypeDescriptor
    {

        private readonly PowerPoint.Shape Shape;
        private readonly Dictionary<string, PropertyDescriptor> Properties;

        public PropertyTagBinder(PowerPoint.Shape shape) {
            Shape = shape;
            Properties = new Dictionary<string, PropertyDescriptor>();

            //Find all the tags and add appopriate PropertyDescriptors to the dictionary
            InitializePropertyDescriptors();
        }

        private void InitializePropertyDescriptors() {
            //Iterate over the list of tags
            foreach(PropertyInfo p in typeof(PropertyTags).GetProperties(BindingFlags.Static | BindingFlags.Public)) {

                string propertyName = (string)p.GetValue(typeof(PropertyTags), null);
                string displayName = propertyName;

                foreach(DisplayNameAttribute dna in p.GetCustomAttributes(typeof(DisplayNameAttribute), false)) {
                    displayName = dna.DisplayName;
                    break;
                }

                List<Attribute> atts = new List<Attribute>();
                foreach(Attribute a in p.GetCustomAttributes(false)) {
                    atts.Add(a);
                }
                if(propertyName == PropertyTags.LAYER_ACTION) {
                    Properties["Action"] = new ActionPropertyDescriptor(Shape, "Action", "Action", new Attribute[0]);
                } else if(propertyName == PropertyTags.PAGE_LINK) {
                    /* No-op */
                } else {
                    TagPropertyDescriptor td = new TagPropertyDescriptor(Shape, propertyName, displayName, atts.ToArray());
                    Properties[propertyName] = td;
                }
            }
            //Properties["Action"] = new ActionPropertyDescriptor(Shape, "Action", "Action", new Attribute[0]);

        }

        #region Implements ICustomTypeDescriptor
        AttributeCollection ICustomTypeDescriptor.GetAttributes() {
            return TypeDescriptor.GetAttributes(this, true);
        }

        string ICustomTypeDescriptor.GetClassName() {
            return TypeDescriptor.GetClassName(this, true);
        }

        string ICustomTypeDescriptor.GetComponentName() {
            return TypeDescriptor.GetComponentName(this, true);
        }

        TypeConverter ICustomTypeDescriptor.GetConverter() {
            return TypeDescriptor.GetConverter(this, true);
        }

        EventDescriptor ICustomTypeDescriptor.GetDefaultEvent() {
            return TypeDescriptor.GetDefaultEvent(this, true);
        }

        PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty() {
            //Return name property

            return null;
        }

        object ICustomTypeDescriptor.GetEditor(Type editorBaseType) {
            return TypeDescriptor.GetEditor(this, editorBaseType, true);
        }

        EventDescriptorCollection ICustomTypeDescriptor.GetEvents() {
            return TypeDescriptor.GetEvents(this, true);
        }

        EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes) {
            return TypeDescriptor.GetEvents(this, attributes, true);
        }

        PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties() {
            return ((ICustomTypeDescriptor)this).GetProperties(new Attribute[0]);
        }

        PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes) {

            List<PropertyDescriptor> props = new List<PropertyDescriptor>();

            foreach(string propertyName in Properties.Keys) {
                props.Add(Properties[propertyName]);
            }

            return new PropertyDescriptorCollection(props.ToArray());
        }

        object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd) {
            return this;
        }

        #endregion Implements ICustomTypeDescriptor



        public class TagPropertyDescriptor : PropertyDescriptor
        {
            private readonly PowerPoint.Shape Shape;

            private string _displayName;
            

            public TagPropertyDescriptor(PowerPoint.Shape shape, string propertyName, string displayName, Attribute[] attrs)
                : base(propertyName, attrs) {
                Shape = shape;
                _displayName = displayName;
            }

            public override string DisplayName {
                get { return _displayName; }
            }

            public override Type ComponentType {
                get { return typeof(PowerPoint.Shape); }
            }

            public override bool IsReadOnly {
                get { return Attributes.Matches(ReadOnlyAttribute.Yes); }
            }

            public override Type PropertyType {
                get { return typeof(string); }
            }

            public override bool CanResetValue(object component) {
                return false; //TODO: Temporarily
            }

            public override object GetValue(object component) {
                //Get the tag here
                try {
                    return Shape.Tags[Name];
                } catch(Exception ex) {
                    Console.WriteLine("Cannot get the object value: {0}", ex.Message);
                    return null;
                }
            }

            public override void ResetValue(object component) {
                SetValue(component, null);
            }

            public override void SetValue(object component, object value) {
                //Set the tag here
                Shape.Tags.Add(Name, value as string);
            }

            public override bool ShouldSerializeValue(object component) {
                return false;//TODO: this is probably always ok
            }


        }

        public class ActionPropertyDescriptor : PropertyDescriptor
        {
            private readonly PowerPoint.Shape Shape;

            private string _displayName;


            public ActionPropertyDescriptor(PowerPoint.Shape shape, string propertyName, string displayName, Attribute[] attrs)
                : base(propertyName, attrs) {
                Shape = shape;
                _displayName = displayName;
            }

            public override string DisplayName {
                get { return _displayName; }
            }

            public override Type ComponentType {
                get { return typeof(PowerPoint.Shape); }
            }

            public override bool IsReadOnly {
                get { return Attributes.Matches(ReadOnlyAttribute.Yes); }
            }

            public override Type PropertyType {
                get { return typeof(NavigationAction); }
            }

            public override bool CanResetValue(object component) {
                return false; //TODO: Temporarily
            }

            public override object GetValue(object component) {
                //Get the tag here
                try {
                    //NavigationAction newAction = new NavigationAction();
                    if(!(string.IsNullOrEmpty(Shape.Tags[PropertyTags.LAYER_ACTION]))) {
                        return ThreeByte.ActiveDeck.Content.SlideUtil.ParseLayerAction(Shape.Tags[PropertyTags.LAYER_ACTION]);
                    } else if(!(string.IsNullOrEmpty(Shape.Tags[PropertyTags.PAGE_LINK]))) {
                        NavigationAction newAction = new NavigationAction();
                        newAction.Jump = true;
                        newAction.Page = int.Parse(Shape.Tags[PropertyTags.PAGE_LINK]);
                        return newAction;
                    } else if(!(string.IsNullOrEmpty(Shape.Tags[PropertyTags.DATA_COMMAND]))) {
                        NavigationAction newAction = new NavigationAction();
                        newAction.Command = true;
                        newAction.CommandData = Shape.Tags[PropertyTags.DATA_COMMAND];
                        return newAction;
                    } else if(!(string.IsNullOrEmpty(Shape.Tags[PropertyTags.BACK_ACTION]))) {
                        NavigationAction newAction = new NavigationAction() { Back = true };
                        return newAction;
                    }
                    return null;
                    //return Shape.Tags[Name];
                } catch(Exception ex) {
                    Console.WriteLine("Cannot get the object value: {0}", ex.Message);
                    return null;
                }
            }

            public override void ResetValue(object component) {
                SetValue(component, null);
            }

            public override void SetValue(object component, object value) {
                //Set the tag here
                NavigationAction action = (NavigationAction)value;
                if(value == null) {
                    Shape.Tags.Delete(PropertyTags.PAGE_LINK);
                    Shape.Tags.Delete(PropertyTags.LAYER_ACTION);
                    Shape.Tags.Delete(PropertyTags.DATA_COMMAND);
                    return;
                }
                if(action.Jump) {
                    Shape.Tags.Add(PropertyTags.PAGE_LINK, string.Format("{0}", action.Page));
                    Shape.Tags.Delete(PropertyTags.LAYER_ACTION);
                    Shape.Tags.Delete(PropertyTags.DATA_COMMAND);
                    Shape.Tags.Delete(PropertyTags.BACK_ACTION);
                } else if(action.Layer) {
                    Shape.Tags.Add(PropertyTags.LAYER_ACTION, string.Format("{0}:{1}", action.State, action.LayerTarget));
                    Shape.Tags.Delete(PropertyTags.PAGE_LINK);
                    Shape.Tags.Delete(PropertyTags.DATA_COMMAND);
                    Shape.Tags.Delete(PropertyTags.BACK_ACTION);
                } else if(action.Command) {
                    Shape.Tags.Add(PropertyTags.DATA_COMMAND, string.Format("{0}", action.CommandData));
                    Shape.Tags.Delete(PropertyTags.PAGE_LINK);
                    Shape.Tags.Delete(PropertyTags.LAYER_ACTION);
                    Shape.Tags.Delete(PropertyTags.BACK_ACTION);
                } else if(action.Back) {
                    Shape.Tags.Add(PropertyTags.BACK_ACTION, string.Format("BACK"));
                    Shape.Tags.Delete(PropertyTags.PAGE_LINK);
                    Shape.Tags.Delete(PropertyTags.LAYER_ACTION);
                    Shape.Tags.Delete(PropertyTags.DATA_COMMAND);
                }
                //TODO:  Implement save, submit, next, back
                //Don't do anything with save, submit, next -- yet
            }

            public override bool ShouldSerializeValue(object component) {
                return false;//TODO: this is probably always ok
            }


        }

    }
}
